import { fluentProvide } from 'inversify-binding-decorators';
import bindingInterfaces from 'inversify-binding-decorators/dts/interfaces/interfaces';
import { interfaces } from 'inversify';
import {
  BreakOrContext,
  hasAspectMetadata,
  MethodAfterAdvice,
  MethodAfterReturningAdvice,
  MethodAfterThrowingAdvice,
  MethodAroundAdvice,
  MethodBeforeAdvice,
  sortAdvice,
} from '../common';

/** 组件作用域 */
export enum Scope {
  /** 瞬时 */
  Transient,
  /** 单列 */
  Singleton,
  /** 请求 */
  Request,
  /** 会话 */
  Session,
}

/** Component装饰器配置 */
export interface ComponentOption {
  /** 组件ID */
  id?: interfaces.ServiceIdentifier<any>;
  /** 作用域 */
  scope?: Scope;
  /** 当前组件是否启用AOP功能，默认值：true */
  enableAop?: boolean;
  // /** ?? */
  // rebind?: boolean;
}

export namespace ComponentOption {
  /** 判断options对象是否是ComponentOption类型 */
  export function is(options: any): options is ComponentOption {
    return options && (options.id !== undefined || options.scope !== undefined || options.rebind !== undefined || options.enableAop !== undefined);
  }
}

/** Component装饰器定义 */
export interface ComponentDecorator {
  (option?: interfaces.ServiceIdentifier<any> | ComponentOption): ClassDecorator;
}

/** Component装饰器 */
const Component = <ComponentDecorator>function (idOrOption?: interfaces.ServiceIdentifier<any> | ComponentOption): ClassDecorator {
  const option = getComponentOption(idOrOption);
  // 返回Class装饰器
  return (target: Function) => {
    applyComponentDecorator(option, target);
  };
};

/**
 * 获取Component装饰器配置
 * @param idOrOption 组件ID或者Component装饰器配置
 */
function getComponentOption(idOrOption?: interfaces.ServiceIdentifier<any> | ComponentOption): ComponentOption {
  let option: ComponentOption = {};
  if (ComponentOption.is(idOrOption)) {
    option = {...idOrOption};
  } else if (idOrOption) {
    option = {id: idOrOption};
  }
  return option;
}

/**
 * 应用Component装饰器
 * @param option Component装饰器配置
 * @param target 目标Class的构造函数
 */
function applyComponentDecorator(option: ComponentOption, target: Function): void {
  // Component装饰器默认配置
  const defaultComponentOption: ComponentOption = {
    id: target,
    scope: Scope.Singleton,
    enableAop: true,
    // rebind: false,
  };
  // Component装饰器配置
  const opt = {...defaultComponentOption, ...option};
  const id = <interfaces.ServiceIdentifier<any>>(opt.id || target);
  const provide = fluentProvide(id);
  let whenOn!: bindingInterfaces.ProvideWhenOnSyntax<any>;
  // 组件作用域
  switch (opt.scope) {
    case Scope.Singleton:
      whenOn = provide.inSingletonScope();
      break;
    case Scope.Transient:
      whenOn = provide.inTransientScope();
      break;
    case Scope.Request:
      // TODO: 组件作用域 Request
      break;
    case Scope.Session:
      // TODO: 组件作用域 Session
      break;
    default:
      whenOn = provide.inSingletonScope();
  }
  // 对象
  whenOn.onActivation((context: interfaces.Context, injectable: any) => {
    let bean: any = injectable;
    // 启用AOP功能
    if (opt.enableAop) {
      bean = doProxy(context, injectable);
    }
    return bean;
  }).done()(target);
  // if (opt.rebind) {
  //   Reflect.defineMetadata(Metadata_Key.rebind, true, target);
  // }
  // ids.shift();
  // if (ids.length > 0) {
  //   Reflect.defineMetadata(Metadata_Key.toService, id, target);
  // }
  // for (const service of ids) {
  //   fluentProvide(<interfaces.ServiceIdentifier<any>>service).done()(target);
  // }
}

/**
 * 组件AOP功能，代理实现
 * @param context     IOC容器Context
 * @param injectable  目标组件
 */
function doProxy(context: interfaces.Context, injectable: any): ProxyConstructor {
  // noinspection JSUnusedLocalSymbols
  const handler: ProxyHandler<any> = {
    /**
     * 拦截对象的读取属性操作
     * @param target    目标实例对象
     * @param property  被获取的属性名
     * @param receiver  Proxy或者继承Proxy的对象
     */
    get: (target: any, property: PropertyKey, receiver: any): any => {
      const propertyValue = target[property];
      if (typeof propertyValue === 'function') {
        // 方法调用拦截
        const originalFunc: Function = <Function>propertyValue;
        return methodAdvice(context, target, property, originalFunc);
      }
      // TODO: 获取属性拦截
      return propertyValue;
    }
  };
  const proxy = new Proxy(injectable, handler);
  proxy.target = injectable;
  injectable.proxyTarget = proxy;
  return proxy;
}

/**
 * 对方法做AOP增强
 * @param context       IOC容器Context
 * @param target        目标实例对象
 * @param property      方法名称
 * @param originalFunc  原始的方法 Function
 * @returns             返回AOP增强了的方法 Function
 */
function methodAdvice(context: interfaces.Context, target: any, property: PropertyKey, originalFunc: Function): Function {
  // 返回代理方法
  return (...args: any[]) => {
    // 方法前置增强
    const methodBeforeList = (context.container.getAll<MethodBeforeAdvice>(MethodBeforeAdvice) || [])
      .filter(advice => advice.filterPointcut && advice.filterPointcut(target, property, args))
      .sort(sortAdvice);
    // 方法正常执行后的后置增强
    const methodAfterReturningList = (context.container.getAll<MethodAfterReturningAdvice>(MethodAfterReturningAdvice) || [])
      .filter(advice => advice.filterPointcut && advice.filterPointcut(target, property, args))
      .sort(sortAdvice);
    // 方法执行异常时的后置增强
    const methodAfterThrowingList = (context.container.getAll<MethodAfterThrowingAdvice>(MethodAfterThrowingAdvice) || [])
      .filter(advice => advice.filterPointcut && advice.filterPointcut(target, property, args))
      .sort(sortAdvice);
    // 方法执行完成之后的增强(无论方法执行是否发生异常)
    const methodAfterList = (context.container.getAll<MethodAfterAdvice>(MethodAfterAdvice) || [])
      .filter(advice => advice.filterPointcut && advice.filterPointcut(target, property, args))
      .sort(sortAdvice);
    // 方法执行前后的环绕增强
    const methodAroundList = (context.container.getAll<MethodAroundAdvice>(MethodAroundAdvice) || [])
      .filter(advice => advice.filterPointcut && advice.filterPointcut(target, property, args))
      .sort(sortAdvice);
    // 环绕增强的上下文变量
    const aroundContextMap: Map<any, BreakOrContext> = new Map<any, BreakOrContext>();
    // 方法前置增强 - 先环绕增强的before，再前置增强的before
    methodAroundList.reverse();
    for (const advice of methodAroundList) {
      if (!advice || !advice.filterPointcut || !advice.before || !advice.after) {
        console.warn("MethodAroundAdvice无效，请继承MethodAroundAdvice，advice -> ", advice)
        continue;
      }
      if (!hasAspectMetadata(advice)) {
        console.warn("MethodAroundAdvice无效，请使用Aspect装饰器，advice -> ", advice)
        continue;
      }
      // 执行方法前置增强
      const aroundContext = advice.before(target, property, args);
      if (aroundContext?.isReturn) {
        // 需要提前返回
        return aroundContext.context;
      }
      aroundContextMap.set(advice, aroundContext);
    }
    for (const advice of methodBeforeList) {
      if (!advice || !advice.filterPointcut || !advice.before) {
        console.warn("MethodBeforeAdvice无效，请继承MethodBeforeAdvice，advice -> ", advice)
        continue;
      }
      if (!hasAspectMetadata(advice)) {
        console.warn("MethodBeforeAdvice无效，请使用Aspect装饰器，advice -> ", advice)
        continue;
      }
      // 执行方法前置增强
      advice.before(target, property, args);
    }
    // 上下文变量
    let returnValue: any;
    let successful = true;
    let exception: any;
    try {
      // 执行原始方法
      returnValue = originalFunc.apply(target, args);
      // 方法正常执行后的后置增强
      for (const advice of methodAfterReturningList) {
        if (!advice || !advice.filterPointcut || !advice.afterReturning) {
          console.warn("MethodAfterReturningAdvice无效，请继承MethodAfterReturningAdvice，advice -> ", advice)
          continue;
        }
        if (!hasAspectMetadata(advice)) {
          console.warn("MethodAfterReturningAdvice无效，请使用Aspect装饰器，advice -> ", advice)
          continue;
        }
        // 执行方法后置增强
        advice.afterReturning(target, property, args, returnValue);
      }
    } catch (error) {
      successful = false;
      exception = error;
      // 方法执行异常时的后置增强
      for (const advice of methodAfterThrowingList) {
        if (!advice || !advice.filterPointcut || !advice.afterThrowing) {
          console.warn("MethodAfterThrowingAdvice无效，请继承MethodAfterThrowingAdvice，advice -> ", advice)
          continue;
        }
        if (!hasAspectMetadata(advice)) {
          console.warn("MethodAfterThrowingAdvice无效，请使用Aspect装饰器，advice -> ", advice)
          continue;
        }
        // 执行方法后置增强
        advice.afterThrowing(target, property, args, error);
      }
    } finally {
      // 方法执行完成之后的增强(无论方法执行是否发生异常) - 先后置增强的after，再环绕增强的after
      for (const advice of methodAfterList) {
        if (!advice || !advice.filterPointcut || !advice.after) {
          console.warn("MethodAfterAdvice无效，请继承MethodAfterAdvice，advice -> ", advice)
          continue;
        }
        if (!hasAspectMetadata(advice)) {
          console.warn("MethodAfterAdvice无效，请使用Aspect装饰器，advice -> ", advice)
          continue;
        }
        // 执行方法后置增强
        advice.after(target, property, args, successful, returnValue, exception);
      }
      methodAroundList.reverse();
      for (const advice of methodAroundList) {
        if (!advice || !advice.filterPointcut || !advice.before || !advice.after) {
          console.warn("MethodAroundAdvice无效，请继承MethodAroundAdvice，advice -> ", advice)
          continue;
        }
        if (!hasAspectMetadata(advice)) {
          console.warn("MethodAroundAdvice无效，请使用Aspect装饰器，advice -> ", advice)
          continue;
        }
        // 执行方法后置增强
        const aroundContext = aroundContextMap.get(advice);
        try {
          const res = advice.after(target, property, args, successful, returnValue, exception, aroundContext?.context);
          if (res === false) {
            // 需要吃掉异常
            successful = true;
            exception = undefined;
          }
        } catch (error) {
          successful = false;
          exception = error;
        }
      }
    }
    if (exception) {
      throw exception;
    }
    return returnValue;
  };
}

export {
  getComponentOption,
  applyComponentDecorator,
  Component,
}
